//################################################################################
// MIT License
// Copyright (c) 2024 ZhangYihua
//
// Change Logs:
// Date           Author       Notes
// 2020-10-14     ZhangYihua   first version
//
// Description  : unsigned data saturation and truncation
//################################################################################

module u_sat_tru #(     // range [0:(2^IDW-1)/(2^IFW)]
parameter           IDW                     = 9,    // input data width
parameter           IFW                     = 5,    // input fractional width,  IFW>=IDW is legal
parameter           ODW                     = 7,    // output data width
parameter           OFW                     = 4,    // output fractional width, OFW>=ODW is legal
parameter           TRU_MODE                = "CBB_DEFINE"  // default truncation mode follows cbb_define.v 
//parameter           TRU_MODE                = "FLOOR"   // discade fractional bits directly for less area and higher Fmax
//parameter           TRU_MODE                = "ROUND"   // discade or carry according to MSB of fractonal bits for better DC
) ( 
input               [IDW-1:0]               id,     // u(IDW, IFW)
output              [ODW-1:0]               od,     // u(ODW, OFW)
output                                      over
);

//################################################################################
// define local varialbe and localparam
//################################################################################
localparam          TW                      = IFW-OFW;      // truncation width
localparam          SW                      = IDW-ODW-TW;   // saturation width

localparam          ROUNDING                = (TRU_MODE=="FLOOR") ? 1'b0 :
                                              (TRU_MODE=="ROUND") ? 1'b1 :
`ifdef TRUNCATION_WITH_ROUNDING
                                                                    1'b1;
`else
                                                                    1'b0;
`endif

//################################################################################
// main
//################################################################################

generate if (ROUNDING==1'b1) begin:G_RND
    wire                [IDW+1-1:0]             tmp;
    wire                [ODW-1:0]               tmp_inc;

    assign tmp = {id, 1'b0} >> TW;
    assign tmp_inc = tmp[1+:ODW]+tmp[0];
    
    if (SW>0) begin:G_NEQ
        assign over = (|id[IDW-1-:SW]) | (&tmp[0+:ODW+1]);
    end else begin:G_EQ
        assign over = &tmp[0+:ODW+1];
    end

    assign od   = (over==1'b1) ? {ODW{1'b1}} : tmp_inc;
end else begin:G_NRND
    if (SW>0) begin:G_NEQ
        assign over = |id[IDW-1-:SW];
        assign od   = (over==1'b0) ? id[TW+:ODW] : {ODW{1'b1}};
    end else begin:G_EQ
        assign od   = id[TW+:ODW];
        assign over = 1'b0;
    end
end endgenerate

//################################################################################
// ASSERTION
//################################################################################

`ifdef CBB_ASSERT_ON
// synopsys translate_off

initial begin
    if (IFW<OFW) begin
        $error("input fractional width is less than output fractional width.");
        $stop;
    end

    if (IDW<ODW) begin
        $error("input total width is less than output total width.");
        $stop;
    end
end

// synopsys translate_on
`endif

endmodule
